#!/usr/bin/perl -w
# elrosi 2 base

use English;
use POSIX;
use Encode;
#use Data::Dumper;	# used for debugging
use LWP;
use XML::Simple;
use List::Util qw(min max);
use URI::Escape;

##########
#
#	Set your priorities here. 
#
##########
@cast_prio = (
# The following is the priority list with the highest prio spell at the top, and the lowest prio at the bottom.
# every time you can cast a spell, we will check from top to bottom and cast the first spell that's available.
# You can choose from the following items:
# fs		: Flame Shock (if not active)
# es3		: Earth Shock with 3 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es4		: Earth Shock with 4 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es5		: Earth Shock with 5 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es6		: Earth Shock with 6 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es7		: Earth Shock with 7 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es8		: Earth Shock with 8 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# es9		: Earth Shock with 9 Lightning Shield charges (only if fs dot lasts longer than shock cd)
# lvb_fs	: Lava Burst if Flame Shock will be active at cast end
# lvb		: Lava Burst without Flame Shock active
# lb		: Lightning Bolt (always include this one, or the sim will hang). No spells should be below Lightning Bolt because they will never be cast.
# ue_lvb	: Unleash Elements (if it benefits Lava Burst)
# ue_fs		: Unleash Elements (if it benefits Flame Shock)
# cl		: Chain Lightning
# fn		: Fire Niva
# eq		: Earthquake
# ts_mana	: Thunderstorm if mana is below 92% (90% glyphed)
# ts		: Thunderstorm
# em		: Elemental Mastery
# st		: Searing Totem
# mt		: Magma Totem
# stomp		: Keep all Totems active
##### following is the default list. you can add spells from the list above or rearrange them as you see fit to try your own priority lists
	"stomp",
	"ts_mana",
	"st",
	"em",
	"fs",
	"lvb_fs",
	#"es7",
	"es8",
	"es9",
	"lb",
);

##########
#
#	Proxy (set your proxy here if you need to use one. If you're not sure, do not change this setting)
#
##########
#$proxy = "http://proxy_adress:proxy_port";

##########
#
#	Everything else will be done interactive.
#
##########

##########
#
#	Welcome to the new ElRoSi
#
##########
print "Welcome to the Elemental Rotation Simulator\n\n";
print "Before loading your character and starting the simulation, we need some information:\n\n";
$answer = "";
while ($answer !~ m/d|dep|n|normal/i) {
	print "Do you want to run in normal mode or do you want to calculate stats weights (dep-mode)? [normal/dep] ";
	$answer = $ARGV[0] || <STDIN>;
	if (chomp($answer) == 0) { print "$answer\n"; }
}
if ($answer =~ m/d|dep/i) {
	$mode = "dep";
} else {
	$mode = "normal";
}
$answer = 0;
while ($answer == 0) {
	print "How many fights do you want to simulate? Recommending 600-1200 to find average dps. ";
	$answer = $ARGV[1] || <STDIN>;
	if (chomp($answer) == 0) { print "$answer\n"; }
}
$fights = $answer;
$answer = 0;
while ($answer == 0) {
	print "How long (in seconds) do you want each fight to take? Recommending 240-300. ";
	$answer = $ARGV[2] || <STDIN>;
	if (chomp($answer) == 0) { print "$answer\n"; }
}
$fighttime = $answer;

$answer = -1;
while (($answer == -1) || ($answer > 40)) {
	print "How far away (in yards) from the mob do you usually stand? Recommending 20. ";
	$answer = $ARGV[3] || <STDIN>;
	if (chomp($answer) == 0) { print "$answer\n"; }
}
$distance = $answer;

##########
#
#	Initializing simulation data
#
##########
$rolltot = 0;
$rolls = 0;

@odprocs = ();
@opprocs = ();
@onpcprocs = ();
@ocprocs = ();
@ocrprocs = ();
@ocfprocs = ();

sub onDamage { foreach $proc (@odprocs) { &{$proc}(); } }				# trinkets/tinkers that proc on any damage

sub onPeriodic { foreach $proc (@opprocs) { &{$proc}(); } }				# trinkets/tinkers that proc on periodic damage

sub onNonperiodicCrit { foreach $proc (@onpcprocs) { &{$proc}(); } }	# trinkets/tinkers that proc on crits, but not periodic crits

sub onCast { foreach $proc (@ocprocs) { &{$proc}(); } }					# trinkets/tinkers etc than can be clicked 

sub onCrit { foreach $proc (@ocrprocs) { &{$proc}(); } }				# trinkets/tinkers that proc on critical strikes

sub onCastFinish { foreach $proc (@ocfprocs) { &{$proc}(); } }			# trinkets/tinkers that proc on finished casts

$racial_haste = 0;
$mailspec = 1;	# will be checked when loading character later on
$speed = 30; # spell travel speed in yards / sec
$lag = 0.05; # lag in seconds
$gcd = 1.5;
@delkeys = ("avgbuyout", "durability", "icon", "displayid", "displayInfoId", "permanentEnchantIcon", "pickUp", "seed", "maxDurability", "level", "rarity", "gemIcon0", "randomPropertiesId", "putDown", "maxcount", "reqlevel", "sellprice", "slotbak", "permanentEnchantSpellDesc", "reqfaction", "reqrep", "dura", "avgbuyout", "gemIcon1", "dmgtype1", "dmgmax1", "dmgmin1", "dps", "feratkpwr", "mledmgmax", "mledmgmin", "mledps", "mlespeed", "speed", "buyprice", "classes", "gemIcon2");
#@reforgestats = ("spi", "critstrkrtng", "hastertng", "hitrtng", "mastrtng");
$browser = LWP::UserAgent->new;
my @ns_headers = (
	'User-Agent' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10',
	'Accept' => 'application/xml',
);
if (defined $proxy) { $browser->proxy("http", $proxy); }
#$browser->show_progress(1);

@stats = ("sta", "int", "agi", "str", "spi", "hastertng", "critstrkrtng", "hitrtng", "mastrtng", "armor", "splpwr");

#$gem = {};

##########
#
#	Initialize buff data
#
##########
$buff = {
	64701	=> {
		name => "Elemental Mastery",
		lasts => 8,
		fallsoff => 0,
		bcharges => 1,
		charges => 0,
	},
	16246	=> {
		name => "Clearcasting",
		lasts => 15,
		fallsoff => 0,
		bcharges => 2,
		charges => 0,
	},
};

my %statnames = (
	sta => "Stamina",
	int => "Intellect",
	agi => "Agility",
	str => "Strength",
	spi => "Spirit",
	hastertng => "Haste Rating",
	critstrkrtng => "Critical Strike Rating",
	hitrtng => "Hit Rating",
	mastrtng => "Mastery Rating",
	armor => "Armor",
	splpwr => "Spellpower",
);

##########
#
#	Initialize spell data
#
##########
$spell = {
	8050	=> {
		name => "Flame Shock",
		cost => 0.17,		#base cost
		bdmg => 531,		#base direct damage
		bdotdmg => 142,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 6,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 18,	#base dot duration
		fallsoff => 0,	#time dot falls off
		ticktime => 3,	#base time between ticks
		coeff => 0.214,		#spell coefficient
		dcoeff => 0.6,		#dot coefficient

		minlvl => 0,	#min level for this spell
	},
	8042	=> {
		name => "Earth Shock",
		cost => 0.18,		#base cost
		bdmg => 931,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 6,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.386,		#spell coefficient
		minlvl => 0,	#min level for this spell
#		prio => sub = { print "ES sub"; },
	},
	51505	=> {
		name => "Lava Burst",
		cost => 0.1,		#base cost
		bdmg => 1586,		#base direct damage	#might be 1441 bdmg instead
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 8,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.571,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	403		=> {
		name => "Lightning Bolt",
		cost => 0.06,		#base cost
		bdmg => 770,		#base direct damage	#might be 770 bdmg instead
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 2,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.714,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	73683	=> {
		name => "Unleash Elements",
		cost => 0.07,		#base cost
		bdmg => 1001,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 15,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.42903,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	421		=> {
		name => "Chain Lightning",
		cost => 0.26,		#base cost
		bdmg => 979,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 3,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.57098,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	1535	=> {
		name => "Fire Nova",
		cost => 0.22,		#base cost
		bdmg => 614,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 10,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.14295,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	61882	=> {
		name => "Earthquake",
		cost => 0.6,		#base cost
		bdmg => 0,		#base direct damage
		bdotdmg => 441,	#base dmg per dot tick
		bcast => 8,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 8,		#base dot duration
		fallsoff => 0,	#time dot falls off
		ticktime => 1,	#base time between ticks
		coeff => 0.14976,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	51490	=> {
		name => "Thunderstorm",
		cost => 0,		#base cost
		bdmg => 1467,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1.5,		#base cast time
		cd => 45,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.57902,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	16166	=> {
		name => "Elemental Mastery",
		cost => 0,		#base cost
		bdmg => 0,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 0,		#base cast time
		cd => 180,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 15,
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	3599	=> {
		name => "Searing Totem",
		cost => 0.05,		#base cost
		bdmg => 0,		#base direct damage
		bdotdmg => 106,	#base dmg per dot tick	might be 106 bdmg instead
		bcast => 1,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 60,	#base dot duration
		fallsoff => 0,	#time dot falls off
		ticktime => 2.4,	#base time between ticks
		coeff => 0.167,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	8190	=> {
		name => "Magma Totem",
		cost => 0.18,		#base cost
		bdmg => 0,		#base direct damage
		bdotdmg => 240,	#base dmg per dot tick
		bcast => 1,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 21,	#base dot duration
		fallsoff => 0,	#time dot falls off
		ticktime => 2,	#base time between ticks
		coeff => 0.06703,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	66842	=> {
		name => "Totemstomp",
		cost => 0,		#base cost
		bdmg => 0,		#base direct damage
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 1,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		lasts => 300,	#standard totem duration
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
	88767	=> {
		name => "Fulmination",
		cost => 0,		#base cost
		bdmg => 375,		#base direct damage # might be 375 bdmg instead?
		bdotdmg => 0,	#base dmg per dot tick
		bcast => 0,		#base cast time
		cd => 0,		#cooldown
		offcd => 0,		#old oncdtill
		fallsoff => 0,	#time dot falls off
		ticktime => 0,	#base time between ticks
		coeff => 0.267,		#spell coefficient
		minlvl => 0,	#min level for this spell
	},
};

#add common keys to spell-hash
foreach $key (keys(%{$spell})) {
	$spell->{$key}->{critmulti} = 1 + ((1.5 * 1.03) - 1) * 2; # crit multiplicator
	$spell->{$key}->{range} = 30;	# range
	$spell->{$key}->{dmg} = 0;		#dmg incl spellpower & buffs
	$spell->{$key}->{ccast} = 0;	#number of casts
	$spell->{$key}->{chit} = 0,		#number of hits
	$spell->{$key}->{chitod} = 0,	#number of overload/dot hits
	$spell->{$key}->{ccrit} = 0,		#number of crits
	$spell->{$key}->{ccritod} = 0,	#number of overload/dot crits
	$spell->{$key}->{cmiss} = 0,	#number of misses
	$spell->{$key}->{cmissod} = 0,	#number of overload/dot misses
	$spell->{$key}->{cdmg} = 0,		#damage done
	$spell->{$key}->{cdmgod} = 0,	#damage done by overload/dots
	$spell->{$key}->{manaspent} = 0,#mana spent
	$spell->{$key}->{prio} = 0,		#spell priority
}
$spell->{trinket} = {
	name => "Trinketprocs",
	ccast => 0,
	chit => 0,
	chitod => 0,
	ccrit => 0,
	ccritod => 0,
	cmiss => 0,
	cmissod => 0,
	cdmg => 0,
	cdmgod => 0,
	manaspent => 0,
	prio => sub { return 0; },
};
$spell->{8050}->{range} = 25;
$spell->{8042}->{range} = 25;
$spell->{51490}->{range} = 10;
$spell->{3599}->{range} = 25;
$spell->{8190}->{range} = 8;
$spell->{1535}->{range} = 10;

##########
#
#	initialize priorities
#
##########
my %cast_prio = (
	fs => 0,						# Flame Shock (if not active)
	es3 => 0,						# Earth Shock with 3 LS charges
	es4 => 0,						# Earth Shock with 4 LS charges
	es5 => 0,						# Earth Shock with 5 LS charges
	es6 => 0,						# Earth Shock with 6 LS charges
	es7 => 0,						# Earth Shock with 7 LS charges
	es8 => 0,						# Earth Shock with 8 LS charges
	es9 => 0,						# Earth Shock with 9 LS charges
	lvb_fs => 0,					# Lava Burst with Flame Shock debuff active
	lvb => 0,						# Lava Burst with no Flame Shock debuff active
	lb => 0,						# Lightning Bolt (spells with lower priority than Lightning Bolt will not be cast! Do not set this to 0, otherwise the sim will loop endlessly)
	ue_lvb => 0,					# Unleash Elements (if it affects Lava Burst)
	ue_fs => 0,						# Unleash Elements (if it affects Flame Shock)
	cl => 0,						# Chain Lightning
	fn => 0,						# Fire Nova
	eq => 0,						# Earth Quake
	ts => 0,						# Thunderstorm
	ts_mana => 0,					# Thunderstorm if mana < 92% (< 90% with glyph)
	em => 0,						# Elemental Mastery
	st => 0,						# Searing Totem
	mt => 0,						# Magma Totem
	stomp => 0,						# Putting down full Totemset
	trinket => 0,					# do not change, just a workaround
);

##########
#
#	Stats
#
##########
@critconv = (45.906, 60.2784, 79.1556, 103.986, 136.53779, 179.280044555664062);
@critint = (500/3, 218.18, 286.51, 376.38, 494.2, 648.91);
@hasteconv = (32.79, 43.056, 56.5397, 74.2755, 97.5272, 128.057159423828125);
@masteryconv = (45.906, 60.2784, 79.1556, 103.986, 136.53799, 179.280044555664062);
@hitconv = (26.232, 34.4448, 45.2318, 59.4204, 78.0218, 102.445739746093750);
@basemana = (4396, 6141, 8581, 11996, 16913, 23430); # value for lvl 84 interpolated using 3144.77 * 1.4^(level-79)
@baseregen = (219, 219, 219, 219, 219, 1219); # values for 81-84 unknown
$basestats = {
	Draenei => {
		str => 132,
		agi => 77,
		sta => 148,
		int => 139,
		spi => 158,
	},
	Dwarf => {
		str => 136,
		agi => 76,
		sta => 149,
		int => 137,
		spi => 155,
	},
	Goblin => {
		str => 0,
		agi => 0,
		sta => 0,
		int => 142,
		spi => 154,
	},
	Troll => {
		str => 0,
		agi => 0,
		sta => 0,
		int => 134,
		spi => 157,
	},
	Orc => {
		str => 0,
		agi => 0,
		sta => 0,
		int => 135,
		spi => 158,
	},
};
@ftw = (211, 0, 0, 0, 0, 747);
$hitcap = 0.17;

##########
#
#	Spell casting subs
#
##########

##########
#
#	Flame Shock subs
#
##########
sub p8050 {	# priority sub
	if ($spell->{8050}->{offcd} <= $now) { # spell is not on cd
		if ($spell->{8050}->{fallsoff} <= $now) { # dot is not active
			#print "FS not on cd and not active, castprio: $cast_prio{fs}\n";
			return $cast_prio{fs};
		} else {
			#print "FS not on cd but active, castprio: 0\n";
			return 0;
		}
	} else {
		#print "FS on cd, castprio: 0\n";
		return 0;
	}
}
sub cs8050 { # cast start sub
	$spell->{8050}->{offcd} = sprintf("%.4f", $now + $spell->{8050}->{cd});
	$spell->{8042}->{offcd} = sprintf("%.4f", $now + $spell->{8042}->{cd});
	$spell->{8050}->{ccast} += 1;
	$cost = $spell->{8050}->{cost} * $basemana[$character->{level}-80] * (1 - $ele_convection * 0.05 - min(1,$buff->{16246}->{charges}) * 0.4);
	$spell->{8050}->{manaspent} += $cost;
	$mana -= $cost;
	onCast();
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$spell->{8050}->{fallsoff} = sprintf("%.4f", $now + $spell->{8050}->{lasts});
		$etime = sprintf("%.4f", $now + $spell->{8050}->{ticktime});
		$event = [$etime, \&ti8050];
		push(@queue, $event); # next tick
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit + $t112 * 0.1) {
			$spell->{8050}->{ccrit} += 1;
			$dmg = $spell->{8050}->{dmg} * $spell->{8050}->{critmulti};
			$spell->{8050}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Flame Shock (%d)\n", $now, $dmg));
			clog(sprintf("%.4f: SPELL_AURA_APPLIED: Flame Shock\n", $now));
			if ($buff->{16246}->{charges} > 0) {
				clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
			} else {
				clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
				calcStatsDamage();
			}
			$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
			$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			onNonperiodicCrit();
			onCrit();
		} else {
			$spell->{8050}->{chit} += 1;
			$dmg = $spell->{8050}->{dmg};
			$spell->{8050}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE: Flame Shock (%d)\n", $now, $dmg));
			clog(sprintf("%.4f: SPELL_AURA_APPLIED: Flame Shock\n", $now));
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
		onDamage();
	} else {
		clog(sprintf("%.4f: SPELL_MISSED: Flame Shock\n", $now));
		$spell->{8050}->{cmiss} += 1;
		if ($buff->{16246}->{charges} > 0) {
			$buff->{16246}->{charges} -= 1;
			if ($buff->{16246}->{charges} == 0) {
				clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
				$buff->{16246}->{fallsoff} = $now;
				calcStatsDamage();
			} else {
				clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
			}
		}
	}
	if ($spell->{16166}->{fallsoff} >= $now) { $buff_em = 1; } else { $buff_em = 0; }
	$spell->{8050}->{dotdmg} = ($spell->{8050}->{bdotdmg} + $spellpower * $spell->{8050}->{dcoeff}) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $buff_em * 0.15);
	$etime = sprintf("%.4f", $now + $spell->{8050}->{cast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCastFinish();
}
sub ti8050 { # tick sub
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit + $t112 * 0.1) {
			$spell->{8050}->{ccritod} += 1;
			$dmg = $spell->{8050}->{dotdmg} * $spell->{8050}->{critmulti};
			$spell->{8050}->{cdmgod} += $dmg;
			clog(sprintf("%.4f: SPELL_PERIODIC_DAMAGE_CRIT: Flame Shock (%d)\n", $now, $dmg));
			onCrit();
		} else {
			$spell->{8050}->{chitod} += 1;
			$dmg = $spell->{8050}->{dotdmg};
			$spell->{8050}->{cdmgod} += $dmg;
			clog(sprintf("%.4f: SPELL_PERIODIC_DAMAGE: Flame Shock (%d)\n", $now, $dmg));
		}
		onDamage();
		onPeriodic();
	} else {
		clog(sprintf("%.4f: SPELL_PERIODIC_MISS: Flame Shock\n", $now));
		$spell->{8050}->{cmissod} += 1;
	}
	if ($ele_lavasurge > 0) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll > 1 - $ele_lavasurge * 0.1) {
			$spell->{51505}->{offcd} = $now;
			clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Lava Surge\n", $now));
		}
	}
	$etime = sprintf("%.4f", $now + $spell->{8050}->{ticktime} / (1 + $haste));
	if ($etime <= $spell->{8050}->{fallsoff}) {
		$event = [$etime, \&ti8050];
		push(@queue, $event); # next tick
	} else {
		clog(sprintf("%.4f: SPELL_AURA_REMOVED: Flame Shock\n", $now));
	}
}
$spell->{8050}->{prio} = \&p8050;
$spell->{8050}->{cssub} = \&cs8050;

##########
#
#	Earth Shock subs
#
##########
sub p8042 { # priority sub
	if (($spell->{8042}->{offcd} <= $now) && ($spell->{8050}->{fallsoff} > $now + $spell->{8050}->{cd})) { # spell is not on cd
		$priokey = "es" . $ls_charges;
		return $cast_prio{$priokey};
	} else {
		#clog("ES on cd, castprio: 0\n");
		return 0;
	}
}
sub cs8042 { # cast start sub
	$spell->{8050}->{offcd} = sprintf("%.4f", $now + $spell->{8050}->{cd});
	$spell->{8042}->{offcd} = sprintf("%.4f", $now + $spell->{8042}->{cd});
	$spell->{8042}->{ccast} += 1;
	$cost = $spell->{8042}->{cost} * $basemana[$character->{level}-80] * (1 - $ele_convection * 0.05 - min(1,$buff->{16246}->{charges}) * 0.4);
	$spell->{8042}->{manaspent} += $cost;
	$mana -= $cost;
	onCast();
	if (($ls_charges > 3) && ($ele_fulmination == 1)) { &{sh88767}; }
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$spell->{8042}->{ccrit} += 1;
			$dmg = $spell->{8042}->{dmg} * $spell->{8042}->{critmulti};
			$spell->{8042}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Earth Shock (%d)\n", $now, $dmg));
			if ($buff->{16246}->{charges} > 0) {
				clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
			} else {
				clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
				calcStatsDamage();
			}
			$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
			$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			onNonperiodicCrit();
			onCrit();
		} else {
			$spell->{8042}->{chit} += 1;
			$dmg = $spell->{8042}->{dmg};
			$spell->{8042}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE: Earth Shock (%d)\n", $now, $dmg));
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
		onDamage();
	} else {
		clog(sprintf("%.4f: SPELL_MISSED: Earth Shock\n", $now));
		$spell->{8042}->{cmiss} += 1;
		if ($buff->{16246}->{charges} > 0) {
			$buff->{16246}->{charges} -= 1;
			if ($buff->{16246}->{charges} == 0) {
				clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
				$buff->{16246}->{fallsoff} = $now;
				calcStatsDamage();
			} else {
				clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
			}
		}
	}
	$etime = sprintf("%.4f", $now + $spell->{8042}->{cast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCastFinish();
}
$spell->{8042}->{prio} = \&p8042;
$spell->{8042}->{cssub} = \&cs8042;

##########
#
#	Lava Burst subs
#
##########
sub p51505 { # priority sub
	if ($spell->{51505}->{offcd} <= $now) { # spell is not on cd			
		$cast = $spell->{51505}->{cast};
		if (($spell->{8050}->{fallsoff} >= $now + $cast) || (($buff->{64701}->{charges} > 0) && ($spell->{8050}->{fallsoff} >= $now))) { # fs active
			#print "LVB not on cd and FS active, castprio: $cast_prio{lvb_fs}\n";
			return $cast_prio{lvb_fs};
		} else {
			#print "LVB not on cd but FS not active, castprio: $cast_prio{lvb}\n";
			return $cast_prio{lvb};
		}
	} else {
		#print "LVB on cd, castprio: 0\n";
		return 0;
	}
}
sub cs51505 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_START: Lava Burst\n", $now));
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{51505}->{cast});
	}
	$event = [$etime, \&cf51505];
	push(@queue, $event); # cast finish event
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd + $lag);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{51505}->{cast} + $lag);
	}
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	if ($buff->{64701}->{charges} > 0) { $buff->{64701}->{charges} = 0; }
	onCast();
}
sub cf51505 { # cast finish sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Lava Burst\n", $now));
	if ($buff->{64701}->{charges} > 0) {
		$spell->{51505}->{offcd} = sprintf("%.4f", $now + $spell->{51505}->{cd} - $gcd);
	} else {
		$spell->{51505}->{offcd} = sprintf("%.4f", $now + $spell->{51505}->{cd});
	}
	$spell->{51505}->{ccast} += 1;
	$cost = $spell->{51505}->{cost} * $basemana[$character->{level}-80] * (1 - $ele_convection * 0.05 - min(1,$buff->{16246}->{charges}) * 0.4);
	$spell->{51505}->{manaspent} += $cost;

	$mana -= $cost;
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		#if ($t10 >= 4) { $spell->{8050}->{fallsoff} += 6; }
		if ($spell->{8050}->{fallsoff} >= $now + $spell->{51505}->{cast}) {
			$mhc = 2;
		} else {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll < $crit) {
				$mhc = 2;
			} else {
				$mhc = 1;
			}
		}
	} else {
		$mhc = 0;
	}
	$etime = sprintf("%.4f", $now + $distance / $speed);
	$event = [$etime, \&sh51505, [$mhc, 0]];
	push(@queue, $event); # spell hit event
	onCastFinish();
}
sub sh51505 { # spell hit sub
	$mhc = @{$_[0]}[0];
	$over = @{$_[0]}[1];
	if ($mhc > 0) {
		if ($over == 0) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll < $overload) {
				$roll = rand;
				$rolltot += $roll;
				$rolls += 1;
				if ($roll > $hitcap - $hit) {
					if ($spell->{8050}->{fallsoff} >= $now + $spell->{51505}->{cast}) {
						$mhc = 2;
					} else {
						$roll = rand;
						$rolltot += $roll;
						$rolls += 1;
						if ($roll < $crit) {
							$mhc = 2;
						} else {
							$mhc = 1;
						}
					}
				} else {
					$mhc = 0;
				}
				$etime = sprintf("%.4f", $now + $distance / $speed);
				$event = [$etime, \&sh51505, [$mhc, 1]];
				push(@queue, $event); # overload event
			}
		}
		if ($mhc > 1) {
			if ($over) {
				$spell->{51505}->{ccritod} += 1;
				$dmg = 0.75 * $spell->{51505}->{dmg} * $spell->{51505}->{critmulti};
				$spell->{51505}->{cdmgod} += $dmg;
			} else {
				$spell->{51505}->{ccrit} += 1;
				$dmg = $spell->{51505}->{dmg} * $spell->{51505}->{critmulti};
				$spell->{51505}->{cdmg} += $dmg;
				if ($buff->{16246}->{charges} > 0) {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
					calcStatsDamage();
				}
				$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
				$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			}
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Lava Burst (%d)\n", $now, $dmg));
			onNonperiodicCrit();
			onCrit();
		} else {
			if ($over) {
				$spell->{51505}->{chitod} += 1;
				$dmg = 0.75 * $spell->{51505}->{dmg};
				$spell->{51505}->{cdmgod} += $dmg;
			} else {
				$spell->{51505}->{chit} += 1;
				$dmg = $spell->{51505}->{dmg};
				$spell->{51505}->{cdmg} += $dmg;
				if ($buff->{16246}->{charges} > 0) {
					$buff->{16246}->{charges} -= 1;
					if ($buff->{16246}->{charges} == 0) {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
						$buff->{16246}->{fallsoff} = $now;
						calcStatsDamage();
					} else {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
					}
				}
			}
			clog(sprintf("%.4f: SPELL_DAMAGE: Lava Burst\n", $now));
		}
		onDamage();
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Lava Burst\n", $now));
		if ($over) {
			$spell->{51505}->{cmissod} += 1;
		} else {
			$spell->{51505}->{cmiss} += 1;
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
	}
}
$spell->{51505}->{prio} = \&p51505;
$spell->{51505}->{cssub} = \&cs51505;

##########
#
#	Lightning Bolt subs
#
##########
sub p403 { # priority sub
	#print "LB, castprio: $cast_prio{lb}\n";
	return $cast_prio{lb};
}
sub cs403 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_START: Lightning Bolt\n", $now));
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{403}->{cast});
	}
	$event = [$etime, \&cf403];
	push(@queue, $event); # cast finish event
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd + $lag);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{403}->{cast} + $lag);
	}
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	if ($buff->{64701}->{charges} > 0) { $buff->{64701}->{charges} = 0; }
	onCast();
}
sub cf403 { # cast finish sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Lightning Bolt\n", $now));
	$spell->{403}->{ccast} += 1;
	$cost = $spell->{403}->{cost} * $basemana[$character->{level}-80] * (1 - $ele_convection * 0.05 - min(1,$buff->{16246}->{charges}) * 0.4);
	$spell->{403}->{manaspent} += $cost;
	$mana -= $cost;
	if ($ele_feedback > 0) {
		$spell->{16166}->{offcd} -= $ele_feedback;
	}
	if ($t10 >= 2) {
		$spell->{16166}->{offcd} -= 1;
	}
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$mhc = 2;
		} else {
			$mhc = 1;
		}
	} else {
		$mhc = 0;
	}
	$etime = sprintf("%.4f", $now + $distance / $speed);
	$event = [$etime, \&sh403, [$mhc, 0]];
	push(@queue, $event); # spell hit event
	onCastFinish();
}
sub sh403 { # spell hit sub
	$mhc = @{$_[0]}[0];
	$over = @{$_[0]}[1];
	if ($mhc > 0) {
		if ($over == 0) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll < $overload) {
				$roll = rand;
				$rolltot += $roll;
				$rolls += 1;
				if ($roll > $hitcap - $hit) {
					$roll = rand;
					$rolltot += $roll;
					$rolls += 1;
					if ($roll < $crit) {
						$mhc = 2;
					} else {
						$mhc = 1;
					}
				} else {
					$mhc = 0;
				}
				$etime = sprintf("%.4f", $now + $distance / $speed);
				$event = [$etime, \&sh403, [$mhc, 1]];
				push(@queue, $event); # overload event
			}
		}
		if ($ele_rollingthunder > 0) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll > 1 - $ele_rollingthunder * 0.3) {
				managain(0.02 * $maxmana, "Rolling Thunder");
				if ($ls_charges < 9) {
					$ls_charges++;
					clog(sprintf("%.4f: SPELL_AURA_APPLIED_DOSE: Lightning Shield (%.0f)\n", $now, $ls_charges));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Lightning Shield\n", $now));
				}
			}
		}
		if ($mhc > 1) {
			if ($over) {
				$spell->{403}->{ccritod} += 1;
				$dmg = 0.75 * $spell->{403}->{dmg} * $spell->{403}->{critmulti};
				$spell->{403}->{cdmgod} += $dmg;
			} else {
				$spell->{403}->{ccrit} += 1;
				$dmg = $spell->{403}->{dmg} * $spell->{403}->{critmulti};
				$spell->{403}->{cdmg} += $dmg;
				if ($buff->{16246}->{charges} > 0) {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
					calcStatsDamage();
				}
				$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
				$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			}
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Lightning Bolt (%d)\n", $now, $dmg));
			onNonperiodicCrit();
			onCrit();
		} else {
			if ($over) {
				$spell->{403}->{chitod} += 1;
				$dmg = 0.75 * $spell->{403}->{dmg};
				$spell->{403}->{cdmgod} += $dmg;
			} else {
				$spell->{403}->{chit} += 1;
				$dmg = $spell->{403}->{dmg};
				$spell->{403}->{cdmg} += $dmg;
				if ($buff->{16246}->{charges} > 0) {
					$buff->{16246}->{charges} -= 1;
					if ($buff->{16246}->{charges} == 0) {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
						$buff->{16246}->{fallsoff} = $now;
						calcStatsDamage();
					} else {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
					}
				}
			}
			clog(sprintf("%.4f: SPELL_DAMAGE: Lightning Bolt (%d)\n", $now, $dmg));
		}
		onDamage();
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Lightning Bolt\n", $now));
		if ($over) {
			$spell->{403}->{cmissod} += 1;
		} else {
			$spell->{403}->{cmiss} += 1;
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
	}
}
$spell->{403}->{prio} = \&p403;
$spell->{403}->{cssub} = \&cs403;

sub p73683 { # unleash elements
	#print "UE dummy\n";
	return 0;
}
$spell->{73683}->{prio} = \&p73683;

##########
#
#	Chain Lightning subs (only single-target atm)
#
##########
sub p421 { # priority sub
	if ($spell->{421}->{offcd} <= $now) { # spell is not on cd
		#print "CL not on cd, castprio: $cast_prio{cl}\n";
		return $cast_prio{cl};
	} else {
		#print "CL on cd, castprio: 0\n";
		return 0;
	}
}
sub cs421 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_START: Chain Lightning\n", $now));
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{421}->{cast});
	}
	$event = [$etime, \&cf421];
	push(@queue, $event); # cast finish event
	if ($buff->{64701}->{charges} > 0) {
		$etime = sprintf("%.4f", $now + $gcd + $lag);
	} else {
		$etime = sprintf("%.4f", $now + $spell->{421}->{cast} + $lag);
	}
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	if ($buff->{64701}->{charges} > 0) { $buff->{64701}->{charges} = 0; }
	onCast();
}
sub cf421 { # cast finish sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Chain Lightning\n", $now));
	$spell->{421}->{offcd} = sprintf("%.4f", $now + $spell->{421}->{cd});
	$spell->{421}->{ccast} += 1;
	$cost = $spell->{421}->{cost} * $basemana[$character->{level}-80] * (1 - $ele_convection * 0.05 - min(1,$buff->{16246}->{charges}) * 0.4);
	$spell->{421}->{manaspent} += $cost;
	$mana -= $cost;
	if ($ele_feedback > 0) {
		$spell->{16166}->{offcd} -= $ele_feedback;
	}
	if ($t10 >= 2) {
		$spell->{16166}->{offcd} -= 1;
	}
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$mhc = 2;
		} else {
			$mhc = 1;
		}
	} else {
		$mhc = 0;
	}
	$etime = $now;
	$event = [$etime, \&sh421, [$mhc, 0]];
	push(@queue, $event); # spell hit event
	onCastFinish();
}
sub sh421 {
	$mhc = @{$_[0]}[0];
	$over = @{$_[0]}[1];
	if ($mhc > 0) {
		if ($over == 0) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll < $overload / 3) {
				$roll = rand;
				$rolltot += $roll;
				$rolls += 1;
				if ($roll > $hitcap - $hit) {
					$roll = rand;
					$rolltot += $roll;
					$rolls += 1;
					if ($roll < $crit) {
						$mhc = 2;
					} else {
						$mhc = 1;
					}
				} else {
					$mhc = 0;
				}
				$etime = $now;
				$event = [$etime, \&sh421, [$mhc, 1]];
				push(@queue, $event); # overload event
			}
		}
		if ($ele_rollingthunder > 0) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll > 1 - $ele_rollingthunder * 0.3) {
				managain(0.02 * $maxmana, "Rolling Thunder");
				if ($ls_charges < 9) {
					$ls_charges++;
					clog(sprintf("%.4f: SPELL_AURA_APPLIED_DOSE: Lightning Shield (%.0f)\n", $now, $ls_charges));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Lightning Shield\n", $now));
				}
			}
		}
		if ($mhc > 1) {
			if ($over) {
				$spell->{421}->{ccritod} += 1;
				$spell->{421}->{cdmgod} += 0.75 * $spell->{421}->{dmg} * $spell->{421}->{critmulti};
			} else {
				$spell->{421}->{ccrit} += 1;
				$spell->{421}->{cdmg} += $spell->{421}->{dmg} * $spell->{421}->{critmulti};
				if ($buff->{16246}->{charges} > 0) {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
					calcStatsDamage();
				}
				$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
				$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			}
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Chain Lightning\n", $now));
			onNonperiodicCrit();
			onCrit();
		} else {
			if ($over) {
				$spell->{421}->{chitod} += 1;
				$spell->{421}->{cdmgod} += 0.75 * $spell->{421}->{dmg};
			} else {
				$spell->{421}->{chit} += 1;
				$spell->{421}->{cdmg} += $spell->{421}->{dmg};
				if ($buff->{16246}->{charges} > 0) {
					$buff->{16246}->{charges} -= 1;
					if ($buff->{16246}->{charges} == 0) {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
						$buff->{16246}->{fallsoff} = $now;
						calcStatsDamage();
					} else {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
					}
				}
			}
			clog(sprintf("%.4f: SPELL_DAMAGE: Chain Lightning\n", $now));
		}
		onDamage();
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Chain Lightning\n", $now));
		if ($over) {
			$spell->{421}->{cmissod} += 1;
		} else {
			$spell->{421}->{cmiss} += 1;
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
	}
}
$spell->{421}->{prio} = \&p421;
$spell->{421}->{cssub} = \&cs421;

sub p1535 { # fire nova
	#print "FN dummy\n";
	return 0;
}
$spell->{1535}->{prio} = \&p1535;

##########
#
#	Earthquake subs
#
##########
sub p61882 { # priority sub
	#print "EQ, castprio: $cast_prio{eq}\n";
	return $cast_prio{eq};
}
sub cs61882 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_START: Earthquake\n", $now));
	for ($n=1;$n<=8;$n++) {
		$etime = sprintf("%.4f", $now + $n * $spell->{61882}->{cast} / 8);
		$event = [$etime, \&ti61882];
		push(@queue, $event); # tick event
	}
	$etime = sprintf("%.4f", $now + $spell->{61882}->{cast});
	$event = [$etime, \&cf61882];
	push(@queue, $event); # cast finish event
	$etime = sprintf("%.4f", $now + $spell->{61882}->{cast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCast();
}
sub cf61882 { # cast finish sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Earthquake\n", $now));
	onCastFinish();
}
sub ti61882 { # tick sub
	clog(sprintf("%.4f: SPELL_PERIODIC_DAMAGE: Earthquake\n", $now));
	onDamage();
}
$spell->{61882}->{prio} = \&p61882;
$spell->{61882}->{cssub} = \&cs61882;

##########
#
#	Thunderstorm subs
#
##########
sub p51490 { # priority sub
	if ($spell->{51490}->{offcd} <= $now) { # spell is not on cd
		if ($mana/$maxmana <= 0.92 - 0.02 * $glyphs->{GlyphofThunderstorm}) {
			#print "TS not on cd and mana below threshold, castprio: $cast_prio{ts_mana}\n";
			return $cast_prio{ts_mana};
		} else {
			#print "TS not on cd but mana above threshold, castprio: $cast_prio{ts}\n";
			return $cast_prio{ts};
		}
	} else {
		#print "TS on cd, castprio: 0\n";
		return 0;
	}
}
sub cs51490 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Thunderstorm\n", $now));
	$spell->{51490}->{ccast} += 1;
	$spell->{51490}->{offcd} = sprintf("%.4f", $now + $spell->{51490}->{cd});
	managain((0.08 + $glyphs->{GlyphofThunderstorm} * 0.02) * $maxmana, "Thunderstorm");
	if ($distance <= $spell->{51490}->{range}) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll > $hitcap - $hit) {
			$roll = rand;
			$rolltot += $roll;
			$rolls += 1;
			if ($roll < $crit) {
				$spell->{51490}->{ccrit} += 1;
				$spell->{51490}->{cdmg} += $spell->{51490}->{dmg} * $spell->{51490}->{critmulti};
				clog(sprintf("%.4f: SPELL_CAST_DAMAGE_CRIT: Thunderstorm\n", $now));
				if ($buff->{16246}->{charges} > 0) {
					clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
				} else {
					clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
					calcStatsDamage();
				}
				$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
				$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
				onNonperiodicCrit();
				onCrit();
			} else {
				$spell->{51490}->{chit} += 1;
				$spell->{51490}->{cdmg} += $spell->{51490}->{dmg};
				clog(sprintf("%.4f: SPELL_CAST_DAMAGE: Thunderstorm\n", $now));
				if ($buff->{16246}->{charges} > 0) {
					$buff->{16246}->{charges} -= 1;
					if ($buff->{16246}->{charges} == 0) {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
						$buff->{16246}->{fallsoff} = $now;
						calcStatsDamage();
					} else {
						clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
					}
				}
			}
			onDamage();
		} else {
			$spell->{51490}->{cmiss} += 1;
			if ($buff->{16246}->{charges} > 0) {
				$buff->{16246}->{charges} -= 1;
				if ($buff->{16246}->{charges} == 0) {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED: Clearcasting\n", $now));
					$buff->{16246}->{fallsoff} = $now;
					calcStatsDamage();
				} else {
					clog(sprintf("%.4f: SPELL_AURA_REMOVED_DOSE: Clearcasting (%.0f)\n", $now, $buff->{16246}->{charges}));
				}
			}
		}
	}
	$etime = sprintf("%.4f", $now + $spell->{51490}->{cast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCast();
	onCastFinish();
}
$spell->{51490}->{prio} = \&p51490;
$spell->{51490}->{cssub} = \&cs51490;

##########
#
#	Elemental Mastery subs
#
##########
sub p16166 { # priority sub
	if ($spell->{16166}->{offcd} <= $now) { # spell is not on cd
		#print "EM not on cd, castprio: $cast_prio{em}\n";
		return $cast_prio{em};
	} else {
		#print "EM on cd, castprio: 0\n";
		return 0;
	}
}
sub cs16166 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Elemental Mastery\n", $now));
	clog(sprintf("%.4f: SPELL_AURA_APPLIED: Elemental Mastery\n", $now));
	$spell->{16166}->{ccast} += 1;
	$spell->{16166}->{offcd} = sprintf("%.4f", $now + $spell->{16166}->{cd});
	$buff->{64701}->{charges} = $buff->{64701}->{bcharges};
	$buff->{64701}->{fallsoff} = sprintf("%.4f", $now + $buff->{64701}->{lasts});
	calcStatsDamage(); # recalc stats for em-buff
	$etime = sprintf("%.4f", $now + $spell->{16166}->{lasts});
	$event = [$etime, \&fa16166];
	push(@queue, $event); # fade event
	$etime = sprintf("%.4f", $now + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCast();
}
sub fa16166 { # fade sub
	clog(sprintf("%.4f: SPELL_AURA_REMOVED: Elemental Mastery\n", $now));
	calcStatsDamage(); # recalc stats for em-buff
}
$spell->{16166}->{prio} = \&p16166;
$spell->{16166}->{cssub} = \&cs16166;

##########
#
#	Searing Totem subs
#
##########
sub p3599 { # priority sub
	if (($spell->{3599}->{fallsoff} <= $now) && ($distance <= $spell->{3599}->{range})) { # dot is not active
		#print "ST not active, castprio: $cast_prio{st}\n";
		return $cast_prio{st};
	} else {
		#print "ST is active, castprio: 0\n";
		return 0;
	}
}
sub cs3599 { # cast start sub
	clog(sprintf("%.4f: UNIT_SUMMON: Searing Totem\n", $now));
	$spell->{3599}->{ccast} += 1;
	$spell->{3599}->{fallsoff} = sprintf("%.4f", $now + $spell->{3599}->{lasts});
	$ticks = int($spell->{3599}->{lasts} / $spell->{3599}->{ticktime});
	for ($n=1;$n<=$ticks;$n++) {
		$etime =sprintf("%.4f", $now + $n * $spell->{3599}->{ticktime} + $distance / $speed);
		$event = [$etime, \&ti3599];
		push(@queue, $event); # tick event
	}
	$etime = sprintf("%.4f", $now + $spell->{3599}->{bcast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCastFinish();
}
sub ti3599 { # tick sub
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$spell->{3599}->{ccritod} += 1;
			$dmg = $spell->{3599}->{dotdmg} * $spell->{3599}->{critmulti};
			$spell->{3599}->{cdmgod} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Searing Bolt (%d)\n", $now, $dmg));
			onCrit();
		} else {
			$spell->{3599}->{chitod} += 1;
			$dmg = $spell->{3599}->{dotdmg};
			$spell->{3599}->{cdmgod} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE: Searing Bolt (%d)\n", $now, $dmg));
		}
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Searing Bolt\n", $now));
		$spell->{3599}->{cmissod} += 1;
	}
}
	
	
sub fa3599 { # fade sub
	clog(sprintf("%.4f: UNIT_DESTROYED: Searing Totem\n", $now));
}
$spell->{3599}->{prio} = \&p3599;
$spell->{3599}->{cssub} = \&cs3599;

##########
#
#	Magma Totem subs
#
##########
sub p8190 { # priority sub
	if ($spell->{8190}->{fallsoff} <= $now) { # dot is not active
		#print "MT not active, castprio: $cast_prio{mt}\n";
		return $cast_prio{mt};
	} else {
		#print "MT is active, castprio: 0\n";
		return 0;
	}
}
sub cs8190 { # cast start sub
	clog(("%.4f: UNIT_SUMMON: Magma Totem\n", $now));
	$spell->{8190}->{ccast} += 1;
	$spell->{8190}->{fallsoff} = sprintf("%.4f", $now + $spell->{8190}->{lasts});
	$ticks = int($spell->{8190}->{lasts} / $spell->{8190}->{ticktime});
	for ($n=1;$n<=$ticks;$n++) {
		$etime = sprintf("%.4f", $now + $n * $spell->{8190}->{ticktime});
		$event = [$etime, \&ti8190];
		push(@queue, $event); # tick event
	}
	$etime = sprintf("%.4f", $now + $spell->{8190}->{bcast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
	onCastFinish();
}
sub ti8190 { # tick sub
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$spell->{8190}->{ccritod} += 1;
			$spell->{8190}->{cdmgod} += $spell->{8190}->{dotdmg} * $spell->{8190}->{critmulti};
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Magma Totem\n", $now));
			onCrit();
		} else {
			$spell->{8190}->{chitid} += 1;
			$spell->{8190}->{cdmgod} += $spell->{8190}->{dotdmg};
			clog(sprintf("%.4f: SPELL_DAMAGE: Magma Totem\n", $now));
		}
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Magma Totem\n", $now));
		$spell->{8190}->{cmiss} += 1;
	}
}
sub fa8190 { # fade sub
	clog(sprintf("%.4f: UNIT_DESTROYED: Magma Totem\n", $now));
}
$spell->{8190}->{prio} = \&p8190;

##########
#
#	Totemstomp subs
#
##########
sub p66842 { # priority sub
	if ($spell->{66842}->{fallsoff} <= $now) { # totems are not active
		#print "Totems not active, castprio: $cast_prio{stomp}\n";
		return $cast_prio{stomp};
	} else {
		#print "Totems are active, castprio: 0\n";
		return 0;
	}
}
sub cs66842 { # cast start sub
	clog(sprintf("%.4f: SPELL_CAST_SUCCESS: Totemstomp\n", $now));
	clog(sprintf("%.4f: UNIT_SUMMON: Totems\n", $now));
	$spell->{66842}->{fallsoff} = sprintf("%.4f", $now + $spell->{66842}->{lasts});
	$etime = sprintf("%.4f", $now + $spell->{66842}->{lasts});
	$event = [$etime, \&fa66842];
	push(@queue, $event);	# fade event totems
	if ((&{$spell->{3599}->{prio}} >= &{$spell->{8190}->{prio}}) && ($distance <= $spell->{3599}->{range})) {
		$spell->{3599}->{ccast} += 1;
		$spell->{3599}->{fallsoff} = sprintf("%.4f", $now + $spell->{3599}->{lasts});
		$ticks = int($spell->{3599}->{lasts} / $spell->{3599}->{ticktime});
		for ($n=1;$n<=$ticks;$n++) {
			$etime =sprintf("%.4f", $now + $n * $spell->{3599}->{ticktime} + $distance / $speed);
			$event = [$etime, \&ti3599];
			push(@queue, $event); # tick event
		}
		$etime = sprintf("%.4f", $now + $spell->{3599}->{lasts});
		$event = [$etime, \&fa3599];
		push(@queue, $event); # fade event searing totem
	} elsif ((&{$spell->{8190}->{prio}} > &{$spell->{3599}->{prio}}) && ($distance <= $spell->{8190}->{range})) {
		$spell->{8190}->{fallsoff} = sprintf("%.4f", $now + $spell->{8190}->{lasts});
		$etime = sprintf("%.4f", $now + $spell->{8190}->{lasts});
		$event = [$etime, \&fa8190];
		push(@queue, $event); # fade event magma totem
	}
	$etime = sprintf("%.4f", $now + $spell->{66842}->{bcast} + $lag);
	$event = [$etime, \&decide];
	push(@queue, $event); # next spell
}
sub fa66842 { # fade sub
	clog(sprintf("%.4f: UNIT_DESTROYED: Totems\n", $now));
}
$spell->{66842}->{prio} = \&p66842;
$spell->{66842}->{cssub} = \&cs66842;

##########
#
#	Fulmination subs
#
##########
sub p88767 { # priority sub
	#print "Fulmi dummy\n";
	return 0;
}
sub sh88767 { # spell hit subroutine
	$spell->{88767}->{ccast} += 1;
	$roll = rand;
	$rolltot += $roll;
	$rolls += 1;
	if ($roll > $hitcap - $hit) {
		$roll = rand;
		$rolltot += $roll;
		$rolls += 1;
		if ($roll < $crit) {
			$spell->{88767}->{ccrit} += 1;
			$dmg = $spell->{88767}->{dmg} * ($ls_charges - 3) * $spell->{88767}->{critmulti};
			$spell->{88767}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE_CRIT: Fulmination@%d (%d)\n", $now, $ls_charges, $dmg));
			if ($buff->{16246}->{charges} > 0) {
				clog(sprintf("%.4f: SPELL_AURA_REFRESH: Clearcasting\n", $now));
			} else {
				clog(sprintf("%.4f: SPELL_AURA_APPLIED: Clearcasting\n", $now));
				calcStatsDamage();
			}
			$buff->{16246}->{charges} = $buff->{16246}->{bcharges};
			$buff->{16246}->{fallsoff} = sprintf("%.4f", $now + $buff->{16246}->{lasts});
			onCrit();
		} else {
			$spell->{88767}->{chit} += 1;
			$dmg = $spell->{88767}->{dmg} * ($ls_charges - 3);
			$spell->{88767}->{cdmg} += $dmg;
			clog(sprintf("%.4f: SPELL_DAMAGE: Fulmination@%d (%d)\n", $now, $ls_charges, $dmg));
		}
	} else {
		clog(sprintf("%.4f: SPELL_MISS: Fulmination\n", $now));
		$spell->{88767}->{cmiss} += 1;
	}
	$ls_charges = 3;
}
$spell->{88767}->{prio} = \&p88767;

##########
#
#	Gets priority for each spell and calls cast sub for highest priority spell
#
##########
sub decide {
	$maxkey = "";
	$maxprio = 0;
	foreach $key (keys(%{$spell})) {
		$prio = &{$spell->{$key}->{prio}};
		if ($prio > $maxprio) {
			$maxprio = $prio;
			$maxkey = $key;
		}
	}
	&{$spell->{$maxkey}->{cssub}};
}

##########
#
#	Sorts Spell-Queue
#
##########
sub sortQueue {
	$a->[0] <=> $b->[0];
}

##########
#
#	Mana Regen. Takes amount and source as arguments.
#
##########
sub managain {
	($amount, $source) = @_;
	$amount = min $maxmana - $mana, $amount;
	$mana += $amount;
	clog(sprintf("%.4f: SPELL_ENERGIZE: %s (%.0f)\n", $now, $source, $amount));
}

##########
#
#	Advances time to next event
#
##########
sub adv_time { # advance time to next event
	@queue = sort { sortQueue(); } @queue; # sort queue
	$event = shift(@queue);
	$oldnow = $now;
	$now = $event->[0]; # set time to time of next event in queue
	$sub = $event->[1]; # stored subroutine
	managain(($now - $oldnow) * $baseregen[$character->{level}-80] / 5, "In Combat Regen");
	if (exists $event->[2]) {
		$par = $event->[2]; # stored parameters
		#print ref $par;
		&{$sub}($par); # call stored subroutine with calles parameters
	} else {
		&{$sub}; # call stored subroutine
	}
}

##########
#
#	Calculates Stats and Spell-Damage. Called e.g. if Trinkets proc or fade
#
##########
sub calcStatsDamage {
	if ($buff->{64701}->{fallsoff} > $now) { $buff_em = 1; } else { $buff_em = 0; }
	if ($buff->{16246}->{charges} > 0) { $buff_cc = 1; } else { $buff_cc = 0; }
	if ($character->{race} eq "Goblin") { $racial_haste = 1; }
	$spellpower = $character->{stats}->{splpwr} + $ftw[$character->{level}-80] * (1 + 0.2 * $enh_elementalweapons) + $buff_sp + $buff_int * 1.05 * (1 + $mailspec * 0.05);
	$haste = (1 + ($character->{stats}->{hastertng} + $buff_haste) / (100 * $hasteconv[$character->{level}-80])) * (1 + 0.05) * (1 + $buff_em * 0.2) * (1 + $racial_goblin * 0.01) - 1;
	$crit = 0.02201 + ($character->{stats}->{critstrkrtng} + $buff_crit) / (100 * $critconv[$character->{level}-80]) + ($character->{stats}->{int} + $buff_int * 1.05 * (1 + $mailspec * 0.05)) / (100 * $critint[$character->{level}-80]) + $ele_acuity * 0.01 + $ele_elementaloath * 0.025 + $glyphs->{GlyphofFlametongueWeapon} * 0.02;
	$overload = 0.16 + 0.02 * int(($character->{stats}->{mastrtng} + $buff_mast) / $masteryconv[$character->{level}-80]);
	$gcd = max 1, 1.5 / (1 + $haste);
	$spell->{8050}->{dmg} = ($spell->{8050}->{bdmg} + $spellpower * $spell->{8050}->{coeff}) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{8050}->{cast} = max $gcd, $spell->{8050}->{bcast} / (1 + $haste);
	$spell->{8042}->{dmg} = ($spell->{8042}->{bdmg} + $spellpower * $spell->{8042}->{coeff}) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{8042}->{cast} = max $gcd, $spell->{8042}->{bcast} / (1 + $haste);
	$spell->{51505}->{dmg} = ($spell->{51505}->{bdmg} + $spellpower * ($spell->{51505}->{coeff} + 0.2)) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $ele_callofflame * 0.05 + $glyphs->{GlyphofLavaBurst} * 0.1 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{51505}->{cast} = max $gcd, $spell->{51505}->{bcast} / (1 + $haste);
	$spell->{403}->{dmg} = ($spell->{403}->{bdmg} + $spellpower * ($spell->{403}->{coeff} + 0.2)) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $glyphs->{GlyphofLightningBolt} * 0.04 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{403}->{cast} = max $gcd, $spell->{403}->{bcast} / (1 + $haste);
	$spell->{421}->{dmg} = ($spell->{421}->{bdmg} + $spellpower * ($spell->{421}->{coeff} + 0.2)) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $buff_em * 0.15) * (1 - $glyphs->{GlyphofChainLightning} * 0.1 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{421}->{cast} = max $gcd, $spell->{421}->{bcast} / (1 + $haste);
	$spell->{61882}->{dotdmg} = ($spell->{61882}->{bdotdmg} + $spellpower * $spell->{61882}->{coeff}) * (1 + $ele_elementalprecision * 0.01 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{61882}->{cast} = max $gcd, $spell->{61882}->{bcast} / (1 + $haste);
	$spell->{51490}->{dmg} = ($spell->{51490}->{bdmg} + $spellpower * $spell->{51490}->{coeff}) * (1 + $ele_concussion * 0.02 + $ele_elementalprecision * 0.01 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{51490}->{cast} = max $gcd, $spell->{51490}->{bcast} / (1 + $haste);
	$spell->{3599}->{dotdmg} = ($spell->{3599}->{bdotdmg} + $spellpower * $spell->{3599}->{coeff}) * (1 + $ele_elementalprecision * 0.01 + $ele_callofflame * 0.05 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{8190}->{dotdmg} = ($spell->{8190}->{bdotdmg} + $spellpower * $spell->{8190}->{coeff}) * (1 + $ele_elementalprecision * 0.01 + $ele_callofflame * 0.05 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{88767}->{dmg} = ($spell->{88767}->{bdmg} + $spellpower * $spell->{88767}->{coeff}) * (1 + $ele_elementalprecision * 0.01 + $enh_improvedshields * 0.05 + $buff_em * 0.15 + $buff_cc * $ele_elementaloath * 0.05);
	$spell->{88767}->{cast} = max $gcd, $spell->{88767}->{bcast} / (1 + $haste);
}

##########
#
#	combatlog
#
##########
sub clog {
	if ($mode ne "dep") { print COMBATLOG $_[0]; }
}

##########
#
#	Simulate Rotation. Takes number and length of fights as arguments
#
##########
sub sim {
	$now = 0;
	# set stats
	$crit = 0.02201 + $character->{stats}->{critstrkrtng} / (100 * $critconv[$character->{level}-80]) + $character->{stats}->{int} / (100 * $critint[$character->{level}-80]) + $ele_acuity * 0.01 + $ele_elementaloath * 0.025;
	$haste = (1 + $character->{stats}->{hastertng} / (100 * $hasteconv[$character->{level}-80])) * (1 + 0.05) * (1 + 0.01 * $racial_goblin) - 1;
	$hit = 0.01 * $racial_draenei + ($character->{stats}->{hitrtng} + ($character->{stats}->{spi} - $basestats->{$character->{race}}->{spi}) * $ele_elementalprecision / 3) / (100 * $hitconv[$character->{level}-80]);
	#if ($character->{race} eq "Draenei") { $hit += 0.01; }
	$overload = int($character->{stats}->{mastrtng} / $masteryconv[$character->{level}-80]) * 0.02 + 0.16;
	$maxmana = $basemana[$character->{level}-80] + ($character->{stats}->{int} - 20) * 15 + 20;
	$spellpower = $character->{stats}->{splpwr} + $ftw[$character->{level}-80] * (1 + 0.2 * $enh_elementalweapons);
	($fights, $fighttime) = @_;
	for ($f=1;$f<=$fights;$f++) {
		#reset trinkets & buffs
		$buff_sp = 0;		# extra spellpower from buffs
		$buff_haste = 0;	# extra haste from buffs
		$buff_crit = 0;		# extra crit from buffs
		$buff_mast = 0;		# extra mastery from buffs
		$buff_int = 0;		# extra intellect from buffs
		calcStatsDamage();
		foreach $key (keys(%{$buff})) {
			#print "resetting $buff->{$key}->{name} ($key)\n";
			$buff->{$key}->{offcd} = 0;
			$buff->{$key}->{fallsoff} = 0;
			$buff->{$key}->{stacks} = 0;
			$buff->{$key}->{upsince} = 0;
			# if (defined $buff->{$key}->{offcd}) { $buff->{$key}->{offcd} = 0; }
			# if (defined $buff->{$key}->{fallsoff}) { $buff->{$key}->{fallsoff} = 0; }
			# if (defined $buff->{$key}->{stacks}) { $buff->{$key}->{stacks} = 0; }
		}
		foreach $key (keys(%{$spell})) {
			$spell->{$key}->{offcd} = 0;
			$spell->{$key}->{fallsoff} = 0;
		}
		$now = 0;
		$mana = $maxmana;
		$ls_charges = 3;
		@queue = ();
		$event = [0, \&decide];
		push(@queue, $event);
		while (($now < $fighttime) && ($mana > 0)) {
			adv_time();
		}
		#print Dumper($buff);
	}
	$totaldmg = 0;
	
	foreach $key (keys(%{$spell})) {
		%sp = %{$spell->{$key}};
		$totaldmg += $sp{cdmg} + $sp{cdmgod};
		if ($name eq "") {
			print sprintf("\n$sp{name}: Casts: %.0f\n", $sp{ccast});
			print sprintf("Normal:       Hits: %.0f | Crits: %.0f | Miss: %.0f\n", $sp{chit}, $sp{ccrit}, $sp{cmiss});
			print sprintf("Overload/Dot: Hits: %.0f | Crits: %.0f | Miss: %.0f\n", $sp{chitod}, $sp{ccritod}, $sp{cmissod});
			print sprintf("Damage:       Normal: %.0f | Overload/Dot: %.0f | Mana spent: %.0f\n", $sp{cdmg}, $sp{cdmgod}, $sp{manaspent});
		}
	}
	if ($name eq "") {
		print "\nBuff Uptimes:\n";
		foreach $key (keys(%{$buff})) {
			if ((exists $buff->{$key}->{uptime}) && ($buff->{$key}->{uptime} > 0)) { print sprintf("$buff->{$key}->{name}: %.2f%% uptime\n", $buff->{$key}->{uptime} * 100 / ($fights * $fighttime)); }
		}
		print sprintf("\n%sDPS: %.2f over %.0f fights of %.0f sec\n", $name, $totaldmg / ($fights * $fighttime), $fights, $fighttime);
	}
	#print sprintf("\n%sDPS: %.2f over %.0f fights of %.0f sec\n", $name, $totaldmg / ($fights * $fighttime), $fights, $fighttime);
	return $totaldmg / ($fights * $fighttime);
}

##########
#
# Import subs
#
##########

sub reforgeById {
	$id = $_[0];
	$refid = $_[1];
	if (exists $reforges->{$refid}) {
		$sstat = $reforges->{$refid}->[0];
		$tstat = $reforges->{$refid}->[1];
		$amount = int($char->{gear}->{item}->{$id}->{$sstat} * 0.4);
		$char->{gear}->{item}->{$id}->{reforge}->{$sstat} = - $amount;
		$char->{gear}->{item}->{$id}->{reforge}->{$tstat} = $amount;
	} else {
		print "Unknown reforge used in $char->{gear}->{item}->{$id}->{name}. ID: $refid\n";
	}
}

sub getStatsById {
	$id = $_[0];
	print "\nGetting data for $id from Wowhead... ";
	$url = "http://www.wowhead.com/item=" . $id . "&xml";
	$response = $browser->get($url, @ns_headers)
		or die "Couldn't get $url -- ", $response->status_line;
	$statsxml = XMLin($response->decoded_content);
	$statstring = $statsxml->{item}->{jsonEquip};
	$statstring =~ s/"//g;
	%itemstats = split(/[:,]/, $statstring);
	foreach $k (keys(%itemstats)) {
		$char->{gear}->{item}->{$id}->{$k} = $itemstats{$k};
	}
	$char->{gear}->{item}->{$id}->{name} = $statsxml->{item}->{name};
	$char->{gear}->{item}->{$id}->{armorclass} = $statsxml->{item}->{subclass}->{id};
	if ($statsxml->{item}->{inventorySlot}->{id} == 6) {
		$char->{gear}->{item}->{$id}->{nsockets} += 1;
		$numsockets = $char->{gear}->{item}->{$id}->{nsockets};
		$socketkey = "socket" . $numsockets;
		$char->{gear}->{item}->{$id}->{$socketkey} = 16;
	}
	@delkeys = ("sellprice", "displayid", "reqlevel", "maxcount", "slotbak", "reqfaction", "reqrep", "dura", "buyprice", "mledps", "mledmgmin", "mledmgmax", "dmgmax1", "dmgtype1", "speed", "dps", "feratkpwr", "mlespeed", "dmgmin1", "reqskillrank", "reqskill", "cooldown");
	foreach $key (@delkeys) {
		delete $char->{gear}->{item}->{$id}->{$key};
	}
	print "Done!\n";
}

sub loadFromBattlenet {
	($name, $realm, $region) = @_;
	$char->{name} = $name;
	$char->{realm} = $realm;
	$char->{region} = $region;
	foreach $stat (@stats) {
		$char->{stats}->{$stat} = 0;
	}
	$char->{stats}->{str} += 132;
	$char->{stats}->{agi} += 77;
	$char->{stats}->{sta} += 148;
	$char->{stats}->{int} += 138;
	$char->{stats}->{spi} += 158;
	%region_prefix = (us => "www", eu => "eu", cn => "cn", kr => "kr", tw => "tw",);
	#$url = "http://$region_prefix{$region}.battle.net/wow/en/character/$realm/$name/simple";
	$url = "http://$region.battle.net/wow/en/character/$realm/$name/simple";
	$response = $browser->get($url, @ns_headers);
	die "Can't get $url -- ", $response->status_line
		unless $response->is_success;
	$battlenet = $response->decoded_content;
	@lines = split(/\n/, $battlenet);
	foreach $line (@lines) {
		if ($line =~ m/wow\/en\/game\/race\//) {
			@raceline = split(/\>/, $line);
			$level = unpack("A2", $raceline[2]);
			$char->{level} = $level;
			$race = reverse(unpack("x3 A*", reverse($raceline[5])));
			$char->{race} = $race;
		} elsif (($line =~ m/wow\/en\/item/) && ($line =~ m/\<img src\=/)) {
			@pieces = split(/"/, $line);
			$iteminfo = $pieces[5];
			$iteminfo =~ s/\&amp//g;
			%iteminfohash = split(/[=;]/, $iteminfo);
			#print $line . "\n";
			$char->{gear}->{item}->{$iteminfohash{i}}->{id} = $iteminfohash{i};
			if (defined $iteminfohash{re}) { $char->{gear}->{item}->{$iteminfohash{i}}->{reforge}->{id} = $iteminfohash{re}; }
			if (defined $iteminfohash{e}) { $char->{gear}->{item}->{$iteminfohash{i}}->{enchant}->{id} = $iteminfohash{e}; }
			if (defined $iteminfohash{ee}) { $char->{gear}->{item}->{$iteminfohash{i}}->{tinker}->{id} = $iteminfohash{ee}; }
			if (defined $iteminfohash{g0}) { $char->{gear}->{item}->{$iteminfohash{i}}->{g0} = $iteminfohash{g0}; }
			if (defined $iteminfohash{g1}) { $char->{gear}->{item}->{$iteminfohash{i}}->{g1} = $iteminfohash{g1}; }
			if (defined $iteminfohash{g2}) { $char->{gear}->{item}->{$iteminfohash{i}}->{g2} = $iteminfohash{g2}; }
		}
	}
	foreach $item (keys(%{$char->{gear}->{item}})) {
		getStatsById($item);
		foreach $stat (@stats) {
			if (defined $char->{gear}->{item}->{$item}->{$stat}) {
				$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{$stat};
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{g0}) {
			if (exists $gemids->{$char->{gear}->{item}->{$item}->{g0}}) {
				$scolor = $char->{gear}->{item}->{$item}->{socket1};
				$char->{gear}->{item}->{$item}->{socket1} = $gemids->{$char->{gear}->{item}->{$item}->{g0}};
				$char->{gear}->{item}->{$item}->{socket1}->{scolor} = $scolor;
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{socket1}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket1}->{$stat};
					}
				}
			} else {
				print "Unknown gem used in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{g0}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{g1}) {
			if (exists $gemids->{$char->{gear}->{item}->{$item}->{g1}}) {
				$scolor = $char->{gear}->{item}->{$item}->{socket2};
				$char->{gear}->{item}->{$item}->{socket2} = $gemids->{$char->{gear}->{item}->{$item}->{g1}};
				$char->{gear}->{item}->{$item}->{socket2}->{scolor} = $scolor;
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{socket2}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket2}->{$stat};
					}
				}
			} else {
				print "Unknown gem used in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{g1}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{g2}) {
			if (exists $gemids->{$char->{gear}->{item}->{$item}->{g2}}) {
				$scolor = $char->{gear}->{item}->{$item}->{socket3};
				$char->{gear}->{item}->{$item}->{socket3} = $gemids->{$char->{gear}->{item}->{$item}->{g2}};
				$char->{gear}->{item}->{$item}->{socket3}->{scolor} = $scolor;
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{socket3}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket3}->{$stat};
					}
				}
			} else {
				print "Unknown gem used in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{g2}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{socketbonus}) {
			if (defined $socketboni->{$char->{gear}->{item}->{$item}->{socketbonus}}) {
				$char->{gear}->{item}->{$item}->{socketbonus} = $socketboni->{$char->{gear}->{item}->{$item}->{socketbonus}};
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{socketbonus}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socketbonus}->{$stat};
					}
				}
			} else {
				print "Unknown socketbonus in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{socketbonus}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{enchant}->{id}) {
			if (exists $enchants->{$char->{gear}->{item}->{$item}->{enchant}->{id}}) {
				$char->{gear}->{item}->{$item}->{enchant} = $enchants->{$char->{gear}->{item}->{$item}->{enchant}->{id}};
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{enchant}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{enchant}->{$stat};
					}
				}
			} else {
				print "Unknown enchant used in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{enchant}->{id}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{tinker}->{id}) {
			if (exists $enchants->{$char->{gear}->{item}->{$item}->{tinker}->{id}}) {
				$char->{gear}->{item}->{$item}->{tinker} = $enchants->{$char->{gear}->{item}->{$item}->{tinker}->{id}};
				foreach $stat (@stats) {
					if (defined $char->{gear}->{item}->{$item}->{tinker}->{$stat}) {
						$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{tinker}->{$stat};
					}
				}
			} else {
				print "Unknown tinker used in $char->{gear}->{item}->{$item}->{name}. ID: $char->{gear}->{item}->{$item}->{tinker}->{id}\n";
			}
		}
		if (defined $char->{gear}->{item}->{$item}->{reforge}->{id}) {
			reforgeById($item, $char->{gear}->{item}->{$item}->{reforge}->{id});
			foreach $stat (@stats) {
				if (defined $char->{gear}->{item}->{$item}->{reforge}->{$stat}) {
					$char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{reforge}->{$stat};
				}
			}
		}
		#print Dumper($char->{gear}->{item}->{$item}) . "\n";
	}
	#talents
	print "Getting primary talent spec... ";
	#$url = "http://$region_prefix{$region}.battle.net/wow/en/character/$realm/$name/talent/primary";
	$url = "http://$region.battle.net/wow/en/character/$realm/$name/talent/primary";
	$response = $browser->get($url, @ns_headers);
	die "Can't get $url -- ", $response->status_line
		unless $response->is_success;
	$talprim = $response->decoded_content;
	print "Done!\n";
	print "Getting secondary talent spec... ";
	#$url = "http://$region_prefix{$region}.battle.net/wow/en/character/$realm/$name/talent/secondary";
	$url = "http://$region.battle.net/wow/en/character/$realm/$name/talent/secondary";
	$response = $browser->get($url, @ns_headers);
	die "Can't get $url -- ", $response->status_line
		unless $response->is_success;
	$talsec = $response->decoded_content;
	print "Done!\n";
	@lines = split(/\n/, $talprim);
	$g = 0;
	foreach $line (@lines) {
		if ($line =~ m/talentcalc\|/) {
			@talentline = split(/\|/, $line);
			@talentline = split(/\"/, $talentline[2]);
			$char->{talents}[0]->{value} = $talentline[0];
			$eletalents = unpack("A19", $char->{talents}[0]->{value});
			$enhtalents = unpack("x19 A19", $char->{talents}[0]->{value});
			$restalents = unpack("x38 A19", $char->{talents}[0]->{value});
			if ((reverse($eletalents) > reverse($enhtalents)) && (reverse($eletalents) > reverse($restalents))) {
				$char->{talents}[0]->{prim} = "Elemental";
			} else {
				$char->{talents}[0]->{prim} = "EnhancerOrResto";
			}
		} elsif ($line =~ m/\|Glyph of /) {
			@glyphline = split(/\|/, $line);
			@glyphline = split(/\"/, $glyphline[2]);
			$char->{talents}[0]->{glyphs}[$g]->{name} = $glyphline[0];
			$g++;
		}
	}
	@lines = split(/\n/, $talsec);
	$g = 0;
	foreach $line (@lines) {
		if ($line =~ m/talentcalc\|/) {
			@talentline = split(/\|/, $line);
			@talentline = split(/\"/, $talentline[2]);
			$char->{talents}[1]->{value} = $talentline[0];
			$eletalents = unpack("A19", $char->{talents}[1]->{value});
			$enhtalents = unpack("x19 A19", $char->{talents}[1]->{value});
			$restalents = unpack("x38 A19", $char->{talents}[1]->{value});
			if ((reverse($eletalents) > reverse($enhtalents)) && (reverse($eletalents) > reverse($restalents))) {
				$char->{talents}[1]->{prim} = "Elemental";
			} else {
				$char->{talents}[1]->{prim} = "EnhancerOrResto";
			}
		} elsif ($line =~ m/\|Glyph of /) {
			@glyphline = split(/\|/, $line);
			@glyphline = split(/\"/, $glyphline[2]);
			$char->{talents}[1]->{glyphs}[$g]->{name} = $glyphline[0];
			$g++;
		}
	}
	$mailspec = 1;
	for $key (keys %{$character->{gear}->{item}}) {
		if ($character->{gear}->{item}->{$key}->{armorclass} =~ m/1|2|4/) {
		#if (("02456789" =~ m/$character->{gear}->{item}->{$key}->{slot}/) && ($character->{gear}->{item}->{$key}->{armorclass} != 3)) {
			#print "$key should be mail but is armorclass $character->{gear}->{item}->{$key}->{armorclass}\n";
			$mailspec = 0;
		} 
	}
	#motw/bok
	$char->{stats}->{sta} = $char->{stats}->{sta} * 1.05;
	$char->{stats}->{int} = $char->{stats}->{int} * 1.05;
	#mailspec
	$char->{stats}->{int} = $char->{stats}->{int} * (1 + $mailspec* 0.05);
	#spellpower
	$char->{stats}->{splpwr} += $char->{stats}->{int};
	$char->{stats}->{splpwr} = $char->{stats}->{splpwr} * 1.1;
	#arcance brilliance
	$char->{stats}->{mana} = 23430 + ($char->{stats}->{int} - 20) * 15 + 20 + 2126;
	return $char;
}

##########
#
#	Save character to file
#
##########
sub saveToFile {
	$xmlout = $_[0];
	$outfile = $_[1];
	open(SAVE, ">:utf8", "$outfile")
		or return -1;
	print SAVE XMLout($xmlout, KeyAttr => {item => 'name'});
}

##########
#
#	Load character from file
#
##########
sub loadFromFile {
	$infile = $_[0];
	open(LOAD, "< $infile")
		or die "Couldn't open $infile: $!";
	$xml = "";
	while ($line = <LOAD>) {
		$xml .= $line;
	}
	$xmlin = XMLin($xml);
	return $xmlin;
}

require "reforges.pl";

require "gems.pl";

require "trinkets.pl";

require "enchants.pl";

##########
#
#	Loading Character (either from file or from armory)
#
##########
@delkeys = ("durability", "icon", "displayid", "displayInfoId", "permanentEnchantIcon", "pickUp", "seed", "maxDurability", "level", "rarity", "gemIcon0", "randomPropertiesId", "putDown", "maxcount", "reqlevel", "sellprice", "slotbak", "permanentEnchantSpellDesc", "reqfaction", "reqrep", "dura", "avgbuyout", "gemIcon1", "dmgtype1", "dmgmax1", "dmgmin1", "dps", "feratkpwr", "mledmgmax", "mledmgmin", "mledps", "mlespeed", "speed", "buyprice", "classes", "gemIcon2");

#print "\nWelcome to Elrosi, the Elemental Rotation Simulator.\n";
$answer = "";
while ($answer !~ m/armory|a|file|f{1}/i) {
	print "Do you want to load you character from the Armory or from file? [a/f] ";
	$answer = $ARGV[4] || <STDIN>;
	if (chomp($answer) == 0) { print "$answer\n"; }
}
if ($answer =~ m/armory|a/i) { #chose armory
	$charsource = "armory";
	print "\nLoading character from the Armory requires character name, Realm and Region.\n";
	print "Character name: ";
	$name = $ARGV[5] || <STDIN>;
	if (chomp($name) == 0) { print "$name\n"; }
	print "Realm:          ";
	$realm = $ARGV[6] || <STDIN>;
	if (chomp($realm) == 0) { print "$realm\n"; }
	$region = "";
	while ($region !~ m/us|eu|cn|tw|kr/i) {
		print "Region (us, eu, cn, tw, kr): ";
		$region = $ARGV[7] || <STDIN>;
		if (chomp($region) == 0) { print "$region\n"; }
	}
	print "\nLoading \u$name @ \u$realm-\U$region... ";
	#$character = loadFromBattlenet(uri_escape_utf8($name), uri_escape_utf8($realm), $region);
	$character = loadFromBattlenet($name, $realm, $region);
	$answer = "";
	while ($answer !~ m/y|yes|n|no/i) {
		print "\nDo you want to save $name to a local file? [y/n] ";
		$answer = $ARGV[8] || <STDIN>;
		if (chomp($answer) == 0) { print "$answer\n"; }
	}
	if ($answer =~ m/y|yes/i) {
		$outfile = "";
		while ($outfile eq "") {
			print "\nFile Name: ";
			$outfile = $ARGV[9] || <STDIN>;
			if (chomp($outfile) == 0) { print "$outfile\n"; }
		}
		saveToFile($character, $outfile);
	}		
}
if ($answer =~ m/file|f/i) { # chose file
	$charsource = "file";
	print "\nLoading character from file requires a saved character.\n";
	$infile = "";
	while ($infile eq "") {
		print "File name: ";
		$infile = $ARGV[5] || <STDIN>;
		if (chomp($infile) == 0) { print "$infile\n"; }
	}
	print "Loading character from $infile.\n";
	$character = loadFromFile($infile);
}

##########
#
#	Checking for Tier 10 pieces
#
##########
$t10 = 0;
foreach $key (keys(%{$character->{gear}->{item}})) {
	$item = $character->{gear}->{item}->{$key};
	if ((exists $item->{itemset}) && (($item->{itemset} == -257) || ($item->{itemset} == -238) || ($item->{itemset} == 893))) { $t10++; }
}
##########
#
#	Checking for Tier 11 pieces
#
##########
$t11 = 0;
foreach $key (keys(%{$character->{gear}->{item}})) {
	$item = $character->{gear}->{item}->{$key};
	if ((exists $item->{itemset}) && (($item->{itemset} == -297) || ($item->{itemset} == 940))) { $t10++; }
}
$t112 = 0;
$t114 = 0;
if ($t11 >= 2) { $t112 = 1; }
if ($t11 >= 4) { $t114 = 1; }

##########
#
#	Checking for Trinkets or other items with procs
#
##########
foreach $key (keys(%{$character->{gear}->{item}})) {
	$item = $character->{gear}->{item}->{$key}->{id};
	if (exists $od{$item}) { push(@odprocs, $od{$item}); }
	if (exists $op{$item}) { push(@opprocs, $op{$item}); }
	if (exists $onpc{$item}) { push(@onpcprocs, $onpc{$item}); }
	if (exists $oc{$item}) { push(@ocprocs, $oc{$item}); }
	if (exists $ocr{$item}) { push(@ocrprocs, $ocr{$item}); }
	if (exists $ocf{$item}) { push(@ocfprocs, $ocf{$item}); }
	if (exists($character->{gear}->{item}->{$key}->{enchant}->{id})) {
		$eid = $character->{gear}->{item}->{$key}->{enchant}->{id};
		#print "proc: $eid\n";
		if (exists($oc{$eid})) { push(@ocprocs, $oc{$eid}); }
		if (exists($ocf{$eid})) { push(@ocfprocs, $ocf{$eid}); }
		if (exists($od{$eid})) { push(@odprocs, $od{$eid}); }
	}
	if (exists($character->{gear}->{item}->{$key}->{tinker}->{id})) {
		$eid = $character->{gear}->{item}->{$key}->{tinker}->{id};
		#print "proc: $eid\n";
		if (exists($oc{$eid})) { push(@ocprocs, $oc{$eid}); }
		if (exists($ocf{$eid})) { push(@ocfprocs, $ocf{$eid}); }
		if (exists($od{$eid})) { push(@odprocs, $od{$eid}); }
	}
	#print Dumper(\@ocp
}

##########
#
#	Checking for race to apply racials
#
##########
$racial_draenei = 0;
$racial_dwarf = 0;
$racial_goblin = 0;
$racial_troll = 0;
$racial_orc = 0;
if ($character->{race} eq "Draenei") { $racial_draenei = 1; }
if ($character->{race} eq "Dwarf") { $racial_dwarf = 1; }
if ($character->{race} eq "Goblin") { $racial_goblin = 1; }
if ($character->{race} eq "Troll") { $racial_troll = 1; }
if ($character->{race} eq "Orc") { $racial_orc = 1; }

##########
#
#	Parsing talentstrings and applying their effects
#
##########
if ($character->{talents}->[0]->{prim} eq "Elemental") {
	$talset = 0;
} elsif ($character->{talents}->[1]->{prim} eq "Elemental") {
	$talset = 1;
}
@allglyphs = ("GlyphofChainLightning", "GlyphofFireElementalTotem", "GlyphofFireNova", "GlyphofFlameShock", "GlyphofFlametongueWeapon", "GlyphofLavaBurst", "GlyphofLightningBolt", "GlyphofLightningShield", "GlyphofShocking", "GlyphofThunder", "GlyphofThunderstorm");
foreach $tglyph (@allglyphs) {
	$glyphs->{$tglyph} = 0;
}
#print Dumper($character->{talents}->[$talset]->{glyphs}) . "\n";
if ($charsource eq "armory") {
	foreach $key (@{$character->{talents}->[$talset]->{glyphs}}) {
		$key =~ s/ //g;
		$glyphs->{$key} = 1;
	}
} elsif ($charsource eq "file") {
	foreach $key (keys(%{$character->{talents}->[$talset]->{glyphs}})) {
		$key =~ s/ //g;
		$glyphs->{$key} = 1;
	}
}
#print Dumper($glyphs);
$ele_acuity = unpack("A1", $character->{talents}->[$talset]->{value});
$ele_convection = unpack("x1 A1", $character->{talents}->[$talset]->{value});
$ele_concussion = unpack("x2 A1", $character->{talents}->[$talset]->{value});
$ele_callofflame = unpack("x3 A1", $character->{talents}->[$talset]->{value});
$ele_reverberation = unpack("x5 A1", $character->{talents}->[$talset]->{value});
$spell->{8050}->{cd} -= 0.5 * $ele_reverberation;
$ele_elementalprecision = unpack("x6 A1", $character->{talents}->[$talset]->{value});
$ele_rollingthunder = unpack("x7 A1", $character->{talents}->[$talset]->{value});
#$ele_elementalfocus = unpack("x8 A1", $character->{talents}->[$talset]->{value});
$ele_elementalreach = unpack("x9 A1", $character->{talents}->[$talset]->{value});
$spell->{403}->{range} += $ele_elementalreach * 5;
$spell->{421}->{range} += $ele_elementalreach * 5;
$spell->{1535}->{range} += $ele_elementalreach * 5;
$spell->{51505}->{range} += $ele_elementalreach * 5;
$spell->{8050}->{range} += $ele_elementalreach * 7.5;
$spell->{8042}->{range} += $ele_elementalreach * 7.5;
$spell->{3599}->{range} += $ele_elementalreach * 7.5;
#$ele_elementaloath = unpack("x10 A1", $character->{talents}->[$talset]->{value});
$ele_elementaloath = 0;
$ele_lavaflows = unpack("x11 A1", $character->{talents}->[$talset]->{value});
$spell->{51505}->{critmulti} = 1 + (1.5 * 1.03 - 1) * (2 + 0.08 * $ele_lavaflows);
$ele_fulmination = unpack("x12 A1", $character->{talents}->[$talset]->{value});
$ele_elementalmastery = unpack("x13 A1", $character->{talents}->[$talset]->{value});
$cast_prio{em} = $ele_elementalmastery * $cast_prio{em};
$ele_feedback = unpack("x16 A1", $character->{talents}->[$talset]->{value});
$ele_lavasurge = unpack("x17 A1", $character->{talents}->[$talset]->{value});
$ele_earthquake = unpack("x18 A1", $character->{talents}->[$talset]->{value});
$cast_prio{eq} = $ele_earthquake * $cast_prio{eq};
$enh_elementalweapons = unpack("x19 A1", $character->{talents}->[$talset]->{value});
$enh_improvedshields = unpack("x21 A1", $character->{talents}->[$talset]->{value});
$enh_totemicreach = unpack("x25 A1", $character->{talents}->[$talset]->{value});
$spell->{3599}->{range} = $spell->{3599}->{range} * (1 + $enh_totemicreach * 0.15);
$spell->{8190}->{range} = $spell->{8190}->{range} * (1 + $enh_totemicreach * 0.15);
$res_totemicfocus = unpack("x* X1 A1", $character->{talents}->[$talset]->{value});
$spell->{3599}->{lasts} = $spell->{3599}->{lasts} * (1 + $res_totemicfocus * 0.2);
$spell->{8190}->{lasts} = $spell->{8190}->{lasts} * (1 + $res_totemicfocus * 0.2);
$spell->{66842}->{lasts} = $spell->{66842}->{lasts} * (1 + $res_totemicfocus * 0.2);

##########
#
#	Adding Glyph's effects
#
##########
#	Prime Glyphs
$spell->{8050}->{lasts} = $spell->{8050}->{lasts} * (1 + $glyphs->{GlyphofFlameShock} * 0.5);
#	Major Glyphs
$spell->{51490}->{cd} -= $glyphs->{GlyphofThunder} * 10;

##########
#
#	Sorting Priority list
#
##########
@cast_prio = reverse(@cast_prio);
for ($n=0;$n<=$#cast_prio;$n++) {
	$cast_prio{$cast_prio[$n]} = $n + 1;
}

##########
#
#	Opening Combatlog for output
#
##########
open(COMBATLOG, ">:utf8", "combatlog.txt")
	or die "Couldn't open combatlog.txt for writing: $!\n";

##########
#
#	Initializing Spell-Queue
#
##########
@queue = ();
$event = [0, \&decide];
push(@queue, $event);

##########
#
#	Starting Rotation
#
##########
if ($mode eq "dep") {
	#prepare pipes, one per child
	pipe(READER0, WRITER0);
	WRITER0->autoflush(1);
	pipe(READER1, WRITER1);
	WRITER1->autoflush(1);
	pipe(READER2, WRITER2);
	WRITER2->autoflush(1);
	pipe(READER3, WRITER3);
	WRITER3->autoflush(1);
	pipe(READER4, WRITER4);
	WRITER4->autoflush(1);
	pipe(READER5, WRITER5);
	WRITER5->autoflush(1);
	pipe(READER6, WRITER6);
	WRITER6->autoflush(1);
	pipe(READER7, WRITER7);
	WRITER7->autoflush(1);
	@amount = (0, 540, 540, 540, 540, 3*ceil($masteryconv[$character->{level}-80]), 540, 540);
	@names = ("Base", "+$amount[1] Intellect", "+$amount[2] Spellpower", "+$amount[3] Haste Rating", "+$amount[4] Critical Strike Rating", "+$amount[5] Mastery Rating", "no hit at all", "hitcapped");
	$name = "Base";
	if ($pid[0] = fork) { # i'm the parent
		close WRITER0;
		$name = "+$amount[1] Intellect";
		$character->{stats}->{int} += $amount[1];
		$character->{stats}->{splpwr} += $amount[1];
		if ($pid[1] = fork) { # i'm still the parent
			close WRITER1;
			$character->{stats}->{int} -= $amount[1];
			$character->{stats}->{splpwr} -= $amount[1];
			$name = "+$amount[2] Spellpower";
			$character->{stats}->{splpwr} += $amount[2];
			if ($pid[2] = fork) { # i'm still the parent
				close WRITER2;
				$character->{stats}->{splpwr} -= $amount[2];
				$name = "+$amount[3] Haste Rating";
				$character->{stats}->{hastertng} += $amount[3];
				if ($pid[3] = fork) { # i'm still the parent
					close WRITER3;
					$character->{stats}->{hastertng} -= $amount[3];
					$name = "+$amount[4] Critical Strike Rating";
					$character->{stats}->{critstrkrtng} += $amount[4];
					if ($pid[4] = fork) {
						close WRITER4;
						$character->{stats}->{critstrkrtng} -= $amount[4];
						$name = "+$amount[5] Mastery Rating";
						$character->{stats}->{mastrtng} += $amount[5];
						if ($pid[5] = fork) {
							close WRITER5;
							$character->{stats}->{mastrtng} -= $amount[5];
							#$name = "+$amount[6] Spirit";
							#$character->{stats}->{spi} += $amount[6];
							$character->{stats}->{spi} = 0;
							$character->{stats}->{hitrtng} = 0;
							if ($pid[6] = fork) {
								close WRITER6;
								# $character->{stats}->{spi} = 0;
								# $character->{stats}->{hitrtng} = 0;
								#$name = "+$amount[7] Hit Rating";
								#$character->{stats}->{hitrtng} += $amount[7];
								$character->{stats}->{hitrtng} = 1742;
								if ($pid[7] = fork) {
									close WRITER7;
									#$character->{stats}->{hitrtng} -= $amount[7];
									#$character->{stats}->{hitrtng} = 1742;
									# $character->{stats}->{spi} = 0;
								} else { # child 7
									die "cannot fork: $!" unless defined $pid[7];
									close READER7;
									print WRITER7 sim($fights, $fighttime);
									close WRITER7;
									exit;
								}
							} else { # child 6
								die "cannot fork: $!" unless defined $pid[6];
								close READER6;
								print WRITER6 sim($fights, $fighttime);
								close WRITER6;
								exit;
							}
						} else { # child 5
							die "cannot fork: $!" unless defined $pid[5];
							close READER5;
							print WRITER5 sim($fights, $fighttime);
							close WRITER5;
							exit;
						}
					} else { # child 4
						die "cannot fork: $!" unless defined $pid[4];
						close READER4;
						print WRITER4 sim($fights, $fighttime);
						close WRITER4;
						exit;
					}
				} else { # i'm child 3
					die "cannot fork: $!" unless defined $pid[3];
					close READER3;
					print WRITER3 sim($fights, $fighttime);
					close WRITER3;
					exit;
				}
			} else { # i'm child 2
				die "cannot fork: $!" unless defined $pid[2];
				close READER2;
				print WRITER2 sim($fights, $fighttime);
				close WRITER2;
				exit;
			}
		} else { # i'm child 1
			die "cannot fork: $!" unless defined $pid[1];
			close READER1;
			print WRITER1 sim($fights, $fighttime);
			close WRITER1;
			exit;
		}
	} else { # i'm child 0
		die "cannot fork: $!" unless defined $pid[0];
		close READER0;
		print WRITER0 sim($fights, $fighttime);
		close WRITER0;
		exit;
	}

	# listen to your children:
	chomp($dps[0] = <READER0>);
	chomp($dps[1] = <READER1>);
	chomp($dps[2] = <READER2>);
	chomp($dps[3] = <READER3>);
	chomp($dps[4] = <READER4>);
	chomp($dps[5] = <READER5>);
	chomp($dps[6] = <READER6>);
	chomp($dps[7] = <READER7>);
	#wait for children to finish
	for ($n=0;$n<=7;$n++) {
		waitpid($pid[$n], 0);
	}
	# print out what the children said
	print sprintf("Base DPS: %.2f\n", $dps[0]);
	for ($n=1;$n<=5;$n++) {
		print sprintf("%s: %.2f (+%.2fdps, %.2f%%)\n", $names[$n], ($dps[$n] - $dps[0]) / $amount[$n], $dps[$n] - $dps[0], $dps[$n]/$dps[0]-1);
	}
	if ($character->{race} eq "Draenei") { $fullhit = 1640; } else { $fullhit = 1742; }
	print sprintf("Hit Rating / Spirit (before cap!): %.2f\n", ($dps[7] - $dps[6]) / $fullhit);
} else {
	$name = "";
	sim($fights, $fighttime);
	print sprintf("\n\nTotal # of random rolls: %d | Average roll: %.8f\n", $rolls, $rolltot / $rolls);
	print sprintf("Expected average roll: %.8f | Deviation: %.8f%%\n", 0.5,  abs(100*(1 - $rolltot / $rolls / 0.5)));
}

print "Press Enter to close this window";
$answer = <STDIN>;
